package rdf

import "fmt"

type Null struct {}

type Selection struct {
  graph *Graph
  subjects []*Node
}

type Set struct {
  subjects []*Node
}

type Graph struct {
  bySubject map[string]SetOfTriple
  byPredicate map[string]SetOfTriple
  // all map[string]*Triple
}


func NewGraph() *Graph {
  return &Graph{make(map[string]SetOfTriple),make(map[string]SetOfTriple)}
}

type Binding map[string]*Node

func (g *Graph) Find( s, p, o Matcher, f func (Binding) ) {
  for _, S := range g.bySubject {
    for _, t := range S {
      if matching( t, s, p, o ) { f( bind( s, p, o, t ) ) }
    }
  }
}

type MatchTriple struct {s, p, o Matcher}

func NewMatchTriple( s, p, o Matcher ) *MatchTriple {
  return &MatchTriple{s, p, o}
}

func (g *Graph) FindAll( tms []*MatchTriple, f func (Binding) ) {
  g.findAll( make(Binding), tms, len(tms), f ) 
}

func (g *Graph) findAll( current Binding, tms []*MatchTriple, i int, f func (Binding) ) {
  if i == 0 {
    f( current )
  } else {
    j := i - 1
    tm := tms[j]
    // fmt.Printf( ">> i = %v, b = %v\n", i, current )
	for _, S := range g.bySubject {
	  for _, t := range S {
	    // fmt.Printf( ">> i = %v, t = %v, current = %v\n", i, t, current )
	    modified := copyMap( current )
	    if matchTriple( modified, t, tm ) {
	      g.findAll( modified, tms, j, f )
	    }
	  }
    }
  }
}

func copyMap( b Binding ) Binding {
  result := make(Binding)
  for k, v := range b { result[k] = v }
  return result
}

func matchTriple( b Binding, t *Triple, tm *MatchTriple ) bool {
  return match( b, t.s, tm.s ) && match( b, t.p, tm.p ) && match( b, t.o, tm.o )
}

func match( b Binding, n *Node, m Matcher ) bool {
  v := m.AsVar()
  if len(v) == 0 {
    if !m.Match( n ) { return false }
  } else {
    here, present := b[v]
    if present && !here.Equals( n ) { return false } else { b[v] = n }
  }
  return true
}

func bind( s, p, o Matcher, t *Triple ) Binding {
  var null string
  b := make(map[string]*Node)
  b[s.AsVar()] = t.s
  b[p.AsVar()] = t.p
  b[o.AsVar()] = t.o
  b[null] = nil, false
  return b
}

func matching( t *Triple, s, p, o Matcher ) bool {
  return s.Match( t.S() ) && p.Match( t.P() ) && o.Match( t.O() )
}

func (g *Graph) Print() {
  fmt.Println( ">> print a graph" )
  // for _, v := range g.all { fmt.Printf( "|> %v\n", v ) }
}

func (g *Graph) Add( t *Triple ) {
  // fmt.Println( ">> adding a triple" )
  striples, sok := g.bySubject[t.S().String()]
  if !sok { striples = NewSetOfTriples(); g.bySubject[t.S().String()] = striples }
  striples.Add( t )
//
  ptriples, pok := g.byPredicate[t.P().String()]
  if !pok { ptriples = NewSetOfTriples(); g.byPredicate[t.P().String()] = ptriples }
  ptriples.Add( t )
  
}

func (g *Graph) SelectSP( s, p *Node ) SetOfTriple {
  fmt.Printf( ">> s, p := %v, %v\n", s, p )
  S, sok := g.bySubject[s.String()]
  P, pok := g.byPredicate[p.String()]
  fmt.Printf( ">> sok, pok = %v, %v\n", sok, pok )
  if !sok || !pok { return NewSetOfTriples() }
  // fmt.Printf( ">> S = %v\n>> P = %v\n", S, P )
  return S.Intersection( P )
}

